<!DOCTYPE html>
<html>

<style>
    #root {
        display: flex;
    }

    #left {
        width: 70%;
        display: inline-block;
    }

    #right {
        display: flex;
        justify-content: flex-start;
        /* 水平左对齐 */
        align-items: flex-start;
        /* 垂直上对齐 */
        margin-left: 5%;
        width: 30%;
        display: inline-block;
    }

    #boxes {
        margin-top: 10px;
        color: white;
    }

    #streams {
        margin-top: 10px;
        color: white;
    }

    #count {
        width: 20px;
        color: white;
    }

    .row {
        display: block;
    }

    .white {
        background-color: azure;
    }

    .blue {
        background-color: aqua;
    }

    .darkcyan {
        background-color: darkcyan;
    }

    .dark {
        color: white;
        background-color: #1f1f1f;
    }

    .small {
        font-size: 12px;
    }

</style>

<body style="background-color: #1f1f1f;">
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root">
        <div id="left" style="background:black">
            <div id="dplayer"></div>
        </div>
        <div id="right">
            <div id="all_pages">
            </div>
            <span class="dark row small">-</span>
            <span class="row">
                <button id="refresh">refresh</button>
                <span id="count"></span>
                <button id="follow" class="white">follow(x)</button>
                <button id="needProxyM3U" class="xblue">porxyM3U(x)</button>
                <button id="needHeader" class="xblue">needHeader(x)</button>
            </span>
            <span id="vtype" class="dark row">
                <span class="dark" >video type:</span>
                <!-- <input name="vtype" id="vtype0" type="radio" class="xblue" answerid="0">
                <input name="vtype" id="vtype1" type="radio" class="xblue" answerid="1">
                <input name="vtype" id="vtype2" type="radio" class="xblue" answerid="2">
                <input name="vtype" id="vtype3" type="radio" class="xblue" answerid="3"> -->
            </span>

            <span id="key" class="dark darkcyan row"></span>
            <span class="dark row small">-</span>
            <span id="info" class="dark row small"></span>
            <span class="dark row small">-</span>
            <span id="info2" class="dark row small"></span>
            <div id="boxes"></div>
            <div id="streams"></div>
        </div>

    </div>
    <!-- <script src="https://cdn.jsdelivr.net/npm/hls.js@1.1.5/dist/hls.min.js" async>
    </script>
    <script src="https://cdn.jsdelivr.net/npm/dplayer@1.26.0/dist/DPlayer.min.js" async>
    </script> -->
    <script src="flv.min.js"></script>
    <script src="hls.min.js"></script>
    <script src="dash.all.min.js"></script>
    <script src="DPlayer.min.js"></script>
    <script>
        let isString = (x) => {
            return typeof x === 'string';
        }

        let isList = (x) => {
            return Array.isArray(x);
        }

        let isObj = (x) => {
            return typeof x === 'object';
        }

        let updateObj = (dst, src) => {
            for (let key in src) {
                dst[key] = src[key]
            }
            return dst;
        }

        const STYLE_BORDER="1px dashed darkcyan";
        // //
        // XX={
        //     data:{a:1},
        //     getData(){
        //         // 等价于 getData:function(){
        //         // return this.data;
        //         //}
        //         return this.data;
        //     },
        //     api_listremotes:"http://v3.tv.server.lan:19999/api/tvbox_box1?do=listremotes",
        //     listremotes:function(){
        //         console.log(this.api_listremotes);
        //         let a=()=>{
        //             // 箭头函数 this 指向上层{}.
        //             console.log(2, this.api_listremotes);
        //         }
        //         let b=function(){
        //             // 当前层.
        //             console.log(3, this.api_listremotes);
        //         }
        //         a();
        //         b();
        //     }
        // }
        // XX.listremotes();
        // XX.getData();
        // //

        const format=(t)=>{
            if(t<10){
                return `0${t}`;
            }
            return `${t}`;
        }

        const get_time_passed=(timestamp)=>{
            try{
                let d=new Date();
                let now=d.getTime();

                let time_passed=parseInt((now-timestamp)/1000); //ms->s
                let time_passed_hour=parseInt(time_passed/3600);
                let time_passed_min=parseInt((time_passed-time_passed_hour*3600)/60);
                let time_passed_sec=parseInt(time_passed%60);

                return `${time_passed_hour}:${format(time_passed_min)}:${format(time_passed_sec)}`;
            }catch(e){
                console.log(e);
                return "";
            }

        }

        const IPS={
            data:{},
            count:0,
            // api_listremotes:"http://v3.tv.server.lan:19999/api/tvbox_box1?do=listremotes",
            api_listremotes:"/api/tvbox_box1?do=listremotes",
            getData(){
                return this.data;
            },
            listremotes(callback){
                this.count++;
                if(this.data){
                    if(this.count>5 && this.count%3!=0){
                        return;
                    }
                }
                fetch(this.api_listremotes).then(
                    r=>{
                        return r.text();
                    }
                ).then(
                    text=>{
                        let ts=text.split("\n");
                        let d=new Date();
                        let now=d.getTime();

                        ts.forEach((t)=>{
                            let tt=t.split(" ");
                            if(tt.length<3)return;
                            if(tt[1]==="timestamp:"){
                                try{
                                    let timestamp=parseInt(tt[2])*1000;
                                    let date=new Date(timestamp);
                                    let year=date.getFullYear();
                                    if(year<2020||year>2200)return;
                                    let time_passed=parseInt((now-timestamp)/1000); //ms->s
                                    let time_passed_hour=parseInt(time_passed/3600);
                                    let time_passed_min=parseInt((time_passed-time_passed_hour*3600)/60);
                                    let time_passed_sec=parseInt(time_passed%60);

                                    this.data[tt[0]]={
                                        timestamp:timestamp,
                                        timeString:`${format(date.getMonth()+1)}/${format(date.getDate())} ${format(date.getHours())}:${format(date.getMinutes())}`,
                                        time_passed:`${time_passed_hour}:${format(time_passed_min)}:${format(time_passed_sec)}`
                                    };
                                }catch{

                                }
                                
                            }
                        });
                    }
                ).finally(
                    ()=>{
                        if(callback){
                            try{
                                callback();
                            }catch{

                            }
                        }
                    }
                );
            }              
        };


        const OBJ = {};// map[string]json_string
        const PLAYING = {
            api: "/api/playing",
            playingKey: null,
            playingObj: null,
            playingUrl: null,
            played:false,
        }
        // let api = "/api/playing"
        // let playingKey = null;
        // let playingObj = null;

        const set_show=(btn,name)=>{
            if(!btn)return;
            btn.show = () => {
                if (btn.yes) {
                    btn.innerText = `${name}(√)`;
                    btn.className = "blue";
                } else {
                    btn.innerText = `${name}(×)`;
                    btn.className = "white";
                }
            };
        }

        let refresh = document.getElementById('refresh');
        let count = document.getElementById('count');
        let follow = document.getElementById('follow');
        follow.yes = localStorage.getItem("follow.yes") == "yes";//自动跟随 json,播放最新的视频
        set_show(follow,"follow");

        let proxyM3U = document.getElementById('needProxyM3U');
        proxyM3U.yes = localStorage.getItem("proxyM3U.yes") == "yes";
        set_show(proxyM3U,"proxyM3U");

        let needHeader = document.getElementById('needHeader');
        needHeader.yes = localStorage.getItem("needHeader.yes") == "yes";
        set_show(needHeader,"needHeader");

        let vtypeBtns = document.getElementById('vtype');
        vtypeBtns.vtype=localStorage.getItem("vtypeBtns.vtype");

        let all_pages = document.getElementById('all_pages');
        let add_page=(text,href)=>{
            let span=document.createElement("span");
            span.className="row";
            let a=document.createElement("a");
            a.target="_blank";
            a.href=href;
            a.style="color:burlywood";
            a.innerText=text;
            span.appendChild(a);
            all_pages.appendChild(span);
        }
        add_page("pushed Page","pushed/index.html");
        add_page("xiaoya","http://x.tv.server.lan:5678/");
        add_page("files","http://tv.server.lan:19999/files/");
        add_page("home page","http://server22.lan:19999/files/homePage_browser/index.html");
        try{
            fetch("pages.json").then((r)=>r.json())
            .then((obj)=>{
                for(let i in obj){
                    let p=obj[i];
                    if(p.url){
                        add_page(p.name,p.url);
                    }
                }
            })
        }catch(e){

        }
        

        let div = document.getElementById('boxes');
        let div_streams = document.getElementById('streams');
        let keyIP = document.getElementById('key');
        let info = document.getElementById('info');
        let info2 = document.getElementById('info2');

        keyIP.style.border=STYLE_BORDER;

        let PLAYER = null;
        const setNullPlayer=()=>{
            PLAYING.played=false;
            PLAYER=null;
        }
        const PIC = "favicon.png";
        let url = "http://server.lan:19999/files/videos/group_11_tv_tv1/09_%E5%B0%84%E9%9B%95_%E7%A5%9E%E9%9B%95/%E5%B0%84%E9%9B%95%E8%8B%B1%E9%9B%84%E4%BC%A0%281983%29/%E5%B0%84%E9%9B%95%E8%8B%B1%E9%9B%84%E4%BC%A0%EF%BC%881983%EF%BC%89S01E02.1080P.mkv";

        const new_player=(video)=>{
            let config={
                container: document.getElementById('dplayer'),
                video: video,
                autoplay: true,
                crossOrigin: 'anonymous',
            };
            if(video.type==="flv"){
                config.flv={
                    enableWorker: true,
                    enableStash: true,
                };
            }
            console.log("[i] config=",config);
            let dp = new DPlayer(config);
            return dp;
        };
        const init_player=(dp)=>{
            const events=[
                "abort",
                "canplay",
                "canplaythrough",
                "durationchange",
                "emptied",
                // "ended",
                "error",
                "loadeddata",
                "loadedmetadata",
                "loadstart",
                "mozaudioavailable",
                "pause",
                "play",
                // "playing",
                // "progress",
                "ratechange",
                "seeked",
                "seeking",
                "stalled",
                "suspend",
                // "timeupdate",
                "volumechange",
                "waiting",
            ];
            dp.on("playing",function(){
                PLAYING.played=true;
            });
            const printInfo=(s)=>{
                console.log(`[event] player: ${s} ==================== <<<< `);
            };
            dp.on("ended",function(){
                printInfo("ended");
                if(PLAYING.played){
                    if(PLAYING.playingKey){
                        //TODO
                        play_by_key(PLAYING.playingKey);
                    }else if(PLAYING.playingUrl){
                        play_by_url(PLAYING.playingUrl);
                    }
                }
            });
            events.forEach((v,i)=>{
                if(!v)return;
                dp.on(v, function () {
                    console.log(`[event] ${i} player: ${v}`);
                });
            });            
        };

        let redirect = (u, callback, type) => {
            fetch(u).then((res) => {
                if (res.status < 200) return u;
                if (res.status >= 200 && res.status < 300) {
                    return u;
                }
                if (resresponse.status < 400) {
                    return res.headers.get('Location');
                }
                return u;
            }).then((url) => {
                let tmp = url.toLowerCase();
                if (tmp.endsWith(".mp4")) {
                    callback(url, "auto")
                    return;
                }
                if (type) {
                    callback(url, type)
                    return;
                }
                callback(url)
            });
        }

        let playedInNewTab = false;
        let playXiaoYa = (u) => {
            playedInNewTab = true;
            const x = ":5678/d/";
            let index = u.indexOf(x);
            let url = u.substring(0, index) + ":5678/" + u.substring(index + x.length);// return "/" + u.substring(index + x.length);
            console.log(url)
            window.open(url, "_blank");
            if (PLAYER) {
                PLAYER.pause();
                PLAYER.notice("需要手动点击 button, 打开新tab");
            }
            // return false;
            return true;
        }

        const URL_proxy = "http://x.tv.server.lan:19999";
        const gen_proxyUrl=(url,headers)=>{
            let u=encodeURIComponent(url);
            let ret=`${URL_proxy}/api/proxy?url=${u}`;
            if(!headers){
                return ret;
            }
            let hd=encodeURIComponent(headers);
            // ret=`${ret}&retry=1&header=${hd}`;
            ret=`${ret}&header=${hd}`;
            return ret;
        }

        let getVideoUrl = (u, header, callback) => {
            playedInNewTab = false;
            let url = u;
            // let index = u.indexOf(URL_proxy + "/");
            // if (index >= 0) {
            //     url=u;
            // }

            // // 本地视频
            // else 
            
            let hd="";
            if(needHeader.yes&&header){
                //// head for proxy
                // hd="&header="+encodeURIComponent(header);
                hd=header;
            }

            if (u.indexOf("server.lan:19999/") >= 0 || u.indexOf("rtmp://obs.server.lan")>=0) {
                if (u.indexOf("server.lan:19999/api/live/") >= 0) {
                    callback(u, "flv");
                    // redirect(u, callback,"auto");
                    return;
                }
                callback(u, "auto");
                return;
                // return "/" + u.substring(index + x.length);
            }
            // xiao ya
            else if (u.indexOf(":5678/d/") >= 0) {
                // http://x.tv.server.lan:5678/%E7%BA%AA%E5%BD%95%E7%89%87/%E8%87%AA%E7%84%B6/%E4%BA%BA%E7%B1%BB%E4%B8%96/%E4%BA%BA%E7%B1%BB%E4%B8%96.%E4%BA%BA%E7%B1%BB%E7%BA%AA.Anthropocene.the.Human.Epoch.2019.1080p.BluRay.x264.mkv
                // callback(u, "auto");
                callback("", "auto");
                if(playXiaoYa(u)){
                    return;
                };
            } else if (u.indexOf(":35455/") > 0) {
                // 需要转发,  (没有cors)
                let type = "flv";
                if (u.indexOf(".m3u") >= 0) {
                    type = "hls";
                }
                url = gen_proxyUrl(u,hd);
                callback(url, type);
                return;
            } else if (u.indexOf(".m3u") > 0) {
                if (proxyM3U.yes) {
                    url = gen_proxyUrl(u,hd);
                } else {
                    url = u;
                }
                let type = "hls";
                callback(url, type);
                return;
            }

            // needHeader.yes
            url = gen_proxyUrl(u,hd);
            callback(url);

            // return location.origin +
            // return u;
        }

        let getVideoType = (url0) => {
            // u.endsWith; x.startsWith
            if(!url0){
                return "auto";
            }
            let url=url0.toLowerCase();
            if (url.indexOf(":19999/api/live/douyu/") > 0) {
                return "flv";
            }
            if (url.indexOf(":19999/api/live/bilibili/") > 0) {
                return "flv";
            }
            if (url.indexOf(".m3u") >= 0) {
                return "hls";
            }
            // TODO
            ///*
            if (url.endsWith(".mp4")||url.endsWith(".mkv")) {
                return "auto";
            }
            if (url.indexOf(".mp4?") > 0 || url.indexOf(".mkv?") > 0) {
                return "auto";
            }
            //*/
            return "flv";
            // if (url.indexOf(".m3u") >= 0) {
            // }
            // return "auto";
            // // return "flv";
        }

        let tc = (func) => {
            // tc = try catch
            try {
                func();
            } catch (error) {

            }
        }
        let prePlayer = (dp) => {
            if (!dp) return;
            tc(() => {
                dp.destroy();
                console.log("dp.destroy");
            });
            setNullPlayer();
            // PLAYER = null;
        }

        let preSwitch = (dp) => {
            if (!dp) return;
            tc(dp.pause);
            tc(() => {
                if (dp.hls) {
                    dp.hls.destroy();
                }
            });
            tc(() => {
                const mediaSource = PLAYER.mediaSource; // 获取 MediaSource 对象
                // 关闭并清除所有 SourceBuffer
                const sourceBuffers = mediaSource.sourceBuffers;
                while (sourceBuffers.length > 0) {
                    mediaSource.removeSourceBuffer(sourceBuffers[0]);
                }
            })
        }

        // let prePlayer = null;
        let play = (url, header) => {
            // let url1 = 
            getVideoUrl(url, header, (url1, type) => {
                let vtype;// = getVideoType(url);
                if (type) {
                    vtype = type;
                } else if (url1.indexOf("tv.server.lan:19999/api") < 0) {
                    vtype = "auto";
                } else {
                    vtype = getVideoType(url);
                }
                // 强制: 手动设置.!
                if(vtypeBtns.vtype){
                    vtype=vtypeBtns.vtype;
                }

                console.log(`[play] type=${vtype}, url=${url}`);

                info.innerText = url;
                info2.innerText = url1;
                // if (prePlayer) {
                //     try {
                //         // 销毁, 不然会一直在load data.
                //         prePlayer.destroy();
                //     } catch (e) {

                //     }
                // }

                let video = {
                    pic: PIC,
                    url: url1,
                    type: vtype,
                };
                prePlayer(PLAYER);
                if (!PLAYER) {
                    let dp = new_player(video);

                    // prePlayer = dp;

                    // dp.on('canplay', function () {
                    //     console.log('player canplay');
                    //     dp.play();
                    // });

                    PLAYER = dp;
                    init_player(dp);
                } else {
                    preSwitch(PLAYER);
                    PLAYER.switchVideo(video);
                }

                PLAYER.notice(url, 2000);
                // dp.on('error', function () {
                //     console.log('player error');
                //     try {
                //         dp.destroy();
                //     } catch (e) {

                //     }

                //     // dp.play();
                // });
                // setTimeout(()=>{

                // },1000);
            });
        }


        let playObj = (obj) => {
            let url0 = obj.playUrl;
            let u = obj.url;
            let url = url0 ? url0 : "";
            try {
                if (isString(u)) {
                    url += u;
                    console.log("string:", url);
                } else if (isObj(u)) {
                    let values = u.values;
                    console.log("obj:", u);

                    if (isList(values)) {
                        console.log("list:values:", values);
                        url += values[0].v
                    } else {
                        url += values.v
                    }
                }
            } catch (e) {
                console.log("[E] playObj err");
                return
            }

            let header = obj.header;
            play(url, header);
        }


        // 1 
        const play_by_key = (key) => {
            let obj = OBJ[key];
            if (obj) {
                keyIP.innerText = key;
                PLAYING.playingKey = key;
                PLAYING.playingObj = obj;
                PLAYING.playingUrl = null;
                playObj(JSON.parse(obj));
            }
        }

        const play_by_url2 = (url) => {
            keyIP.innerText = "直播";
            PLAYING.playingKey = null;
            PLAYING.playingObj = null;
            PLAYING.playingUrl = url;
            play(url);
        }

        // 2
        const play_by_url = (url,type) => {
            // play url : http-flv
            if (url) {
                if(!type){
                    if(vtypeBtns.vtype){
                        type=vtypeBtns.vtype;
                    }else{
                        type="auto";
                    }
                }
                keyIP.innerText = "直播";
                PLAYING.playingKey = null;
                PLAYING.playingObj = null;
                PLAYING.playingUrl = url;

                info.innerText = url;
                info2.innerText = "";

                let video = {
                    pic: PIC,
                    url: url,
                    type: type,
                };
                prePlayer(PLAYER);
                if (!PLAYER) {
                    let dp = new_player(video);
                    PLAYER = dp;
                    init_player(dp);
                } else {
                    preSwitch(PLAYER);
                    PLAYER.switchVideo(video);
                }

                PLAYER.notice(url, 2000);
            }

        }
        

        let is_sameVideo = (key) => {
            if (PLAYING.playingKey && key == PLAYING.playingKey) {
                let obj = OBJ[PLAYING.playingKey];
                let tag = "same:";
                if (obj == PLAYING.playingObj) {
                    return true;
                }
            }
            return false;
        }

        let countX = 0;
        let updateTime = (n) => {
            if (n % 3 == 0) {
                const d = new Date();
                const hour = d.getHours();
                const min = d.getMinutes();
                count.innerText = `${hour.toString().padStart(2, '0')}:${min.toString().padStart(2, '0')}`;
            }
        }

        let upatePlayingState = () => {
            if (PLAYING.playingKey) {
                let obj = OBJ[PLAYING.playingKey];
                let tag = "same:";
                if (obj == PLAYING.playingObj) {
                } else {
                    if (follow.yes) {
                        tag = "not same:"
                        play_by_key(PLAYING.playingKey);
                        console.log(tag, PLAYING.playingKey, obj);
                    }
                }
                countX++;
                updateTime(countX);
            }
        }

        let btns = {};
        let btns_streams = {};
        btns.clearChild = () => {
            while (div.firstChild) {
                div.removeChild(div.firstChild);
            }
        }
        btns_streams.clearChild = () => {
            while (div_streams.firstChild) {
                div_streams.removeChild(div_streams.firstChild);
            }
            let h2=document.createElement("h2");
            h2.style.color="white";
            h2.textContent="lives:";
            div_streams.appendChild(h2);
        }

        btns_streams.addInput = () => {
            let span = document.createElement("span")

            let el0 = document.createElement("button")
            let el1 = document.createElement("button")
            let input = document.createElement("input")
            span.className = "row";
            span.appendChild(el0);
            span.appendChild(el1);
            span.appendChild(input);

            el0.innerText =`play url`;
            el1.innerText =`@proxy`;

            div_streams.appendChild(span);
            let click = (n,btn) => {
                let url=input.value;
                if(!url)return;
                
                if (btns.pre) {
                    btns.pre.classList.remove("blue");
                }
                
                if(btn){
                    btn.className += " blue";
                    btns.pre = btn;
                }
                if (proxyM3U.yes) {
                    url = gen_proxyUrl(url);
                }
                if(n===2){
                    play_by_url2(url);
                }else{
                    play_by_url(url,null);
                }
            };

            // play
            el0.addEventListener("click", (ev) => {
                click(1,el0);
            });
            el1.addEventListener("click", (ev) => {
                click(2,el1);
            });
        }

        btns_streams.add_lives = () => {
            let span = document.createElement("span");
            span.className = "row";
            let input = document.createElement("input");
            input.style.className ="row";
            const click = (btn,type) => {
                if(!btn)return;
                let id=input.value;
                if(!id)return;
                let url=btn.getUrl(id);
                if(!type)type="flv";
                if (btns.pre) {
                    btns.pre.classList.remove("blue");
                }

                btn.className += " blue";
                btns.pre = btn;
                
                // if (proxyM3U.yes) {
                url = gen_proxyUrl(url);
                // }

                btn.play(url,type);

            };
            const createBtn=(name,type,getUrl,play)=>{
                let btn = document.createElement("button");
                btn.textContent=name;

                btn.play=play?play:play_by_url;
                //play_by_url(url,type);
                
                if(getUrl){
                    btn.getUrl=(id)=>{
                        return getUrl(id);
                    };
                }else{
                    btn.getUrl=(id)=>{
                        return `http://x.tv.server.lan:35455/${name}/${id}`;
                    };
                }
                // btn.video_type=type;
                span.appendChild(btn);
                btn.addEventListener("click",()=>{
                    click(btn,type);
                });
                return btn;
            }
            createBtn("douyin","flv");
            createBtn("bilibili","flv",(id)=>{
                // feiyang allinone 的bilibili 无法播放
                return `http://x.tv.server.lan:19999/api/live/bilibili/${id}`;
            });
            // ,(url,type)=>{
            //     play_by_url2(url);
            // });
            createBtn("douyu","flv");
            createBtn("huya","flv");
            createBtn("yy","flv");

            span.appendChild(input);
            div_streams.appendChild(span);
        }

        btns.appendChild = (key,datas,infos) => {
            let obj,time,time_passed;
            try{
                obj=datas[key];
            }catch{
                return;
            }
            try{
                let info=infos[key];
                time=info.timeString;
                time_passed=info.time_passed;
                if(time_passed){
                    time_passed=get_time_passed(info.timestamp);
                }
            }catch{
                time="?";
                time_passed="";
            }
            let jo = JSON.parse(obj);
            
            let el0 = document.createElement("button")
            let span_key = document.createElement("span")
            let span_info = document.createElement("span")
            let span_info2 = document.createElement("span")
            let el = document.createElement("button")

            el.className = "row";
            let span = document.createElement("span");
            span.className = "row";
            span.style.marginTop="6px";
            span.appendChild(el0);
            span.appendChild(span_key);
            span.appendChild(span_info);
            span.appendChild(span_info2);
            span.appendChild(el);

            el0.innerText = "🔄️";
            span_key.style.border=STYLE_BORDER;
            span_key.textContent=`${key}`;
            span_info.style="color:#03a7edcc;font-size:14px;margin-left:2px";
            span_info.textContent=`${time}`;
            span_info2.style="color:#03a7edaa;font-size:12px;margin-left:2px";
            span_info2.textContent=` (time_passed=${time_passed})`;
            el.innerText=`${jo.name}`;
            // if(time){
            //     el.innerText =`${key} : ${time} : ${jo.name}`;
            // }else{
            //     el.innerText =`${key} : ? : ${jo.name}`;
            // }

            if (key == PLAYING.playingKey) {
                el.className += " blue";
                btns.pre = el;
            }

            div.appendChild(span);
            let click = () => {
                if (btns.pre) {
                    btns.pre.classList.remove("blue");
                }
                el.className += " blue";
                btns.pre = el;
                console.log(jo.name);
                play_by_key(key);
            };

            el0.addEventListener("click", (ev) => {
                click();
            });

            el.addEventListener("click", (ev) => {
                if (!playedInNewTab) {
                    if (PLAYER && is_sameVideo(key)) {
                        if (PLAYER.video.paused) {
                            PLAYER.play();
                        }
                        return;
                    }
                }
                click();
            });
        }

        btns_streams.appendChild = (name,url,genUrl) => {
            if(!url){
                return;
            }
            let span = document.createElement("span");

            let el0 = document.createElement("button");
            let el;
            if(genUrl){
                el = document.createElement("input");
            }else{
                el = document.createElement("button");
                el.innerText = url;
            }
            span.className = "row";
            span.appendChild(el0);
            span.appendChild(el);

            el0.innerText =`🚁 ${name}`;
            
            if (url == PLAYING.playingUrl) {
                el0.className += " blue";
                btns.pre = el0;
            }

            div_streams.appendChild(span);

            let click = (getUrl) => {
                if (btns.pre) {
                    btns.pre.classList.remove("blue");
                }
                el0.className += " blue";
                btns.pre = el0;
                play_by_url(getUrl(),"flv");
            };


            let getUrl;
            if(genUrl){
                getUrl=()=>{
                    return genUrl(el.value);
                };
            }else{
                getUrl=()=>{
                    return url;
                };
                el.addEventListener("click", (ev) => {
                    // if (!playedInNewTab) {
                    //     if (PLAYER && is_sameVideo(key)) {
                    //         if (PLAYER.video.paused) {
                    //             PLAYER.play();
                    //         }
                    //         return;
                    //     }
                    // }
                    click(getUrl);
                });
            }
            el0.addEventListener("click", (ev) => {
                // refresh
                click(getUrl);
            });
        }

        const streams=[];
        btns_streams.clearChild();
        btns_streams.add_lives();
        btns_streams.addInput();
        for(let i=0;i<=10;i++){
            let n=i;
            let s;
            if(n==0){
                n="";
            }
            s={
                name:`stream ${n}`,
                url:`http://server.lan:18888/live/livestream${n}.flv`
            };

            
            let genUrl=null;
            if(i==10){
                s.name="streamX";
                genUrl=(name)=>{
                    if(!name.endsWith(".flv")){
                        name=`${name}.flv`;
                    }
                    return `http://server.lan:18888/live/${name}`;
                }
            }
            streams.push(s);
            btns_streams.appendChild(s.name,s.url,genUrl);
        }



        let act = (playNew) => {
            fetch(PLAYING.api).then(
                res => res.json()
            ).then(jo => {
                // console.log(jo);
                updateObj(OBJ, jo)
                jo = OBJ;
                let length = Object.keys(jo).length;
                if (length < 1) return;

                btns.clearChild();

                for (let key in jo) {
                    let obj = jo[key];
                    // console.log(key);
                    if (playNew) {
                        playNew = false;
                        play_by_key(key);
                    }
                    btns.appendChild(key,OBJ,IPS.getData());
                }

                if (!playNew) {
                    upatePlayingState();
                }
                // if (length == 1) {
                //     playObj(JSON.parse(obj0))
                // }
            }).catch(err => {
                console.log("[e] fetch error:", err);
            });
        }
        const upate_state=(callback) => {
            try{
                IPS.listremotes(callback);
            }catch{

            };
            setTimeout(()=>{
                try{
                    // act(true); // 自动播放.
                    act(false);
                }catch{

                }
            },1000);
        }

        upate_state(()=>{
            // act(true); // 自动播放.
            act(false);
        });

        refresh.addEventListener("click", () => {
            act(false);
        });

        follow.addEventListener("click", () => {
            follow.yes = !follow.yes;
            localStorage.setItem("follow.yes", follow.yes ? "yes" : "");
            follow.show();
        });
        follow.show();

        proxyM3U.addEventListener("click", () => {
            proxyM3U.yes = !proxyM3U.yes;
            localStorage.setItem("proxyM3U.yes", proxyM3U.yes ? "yes" : "");
            proxyM3U.show();
        });
        proxyM3U.show();

        vtypeBtns.vTypes=[null,"auto","hls","flv"];
        vtypeBtns.check=(btn)=>{
            if(!btn)return;
            if(vtypeBtns.preChecked){
                vtypeBtns.preChecked.className="";
            }
            vtypeBtns.preChecked=btn;
            btn.className="darkcyan";
        }
        vtypeBtns.vTypes.forEach((v,i)=>{
            //vtypeBtns
            let spanOuter=document.createElement("span");
            let btn=document.createElement("input");
            let span=document.createElement("span");
            btn.type="radio";
            btn.name="vtype";
            spanOuter.style.marginRight="10px";
            spanOuter.style.border=STYLE_BORDER;
            
            if(v){
                btn.value=v;
                span.textContent=v;
            }else{
                btn.value="---";
                span.textContent="---";
            }
            btn.addEventListener("click", () => {
                vtypeBtns.vtype=v;
                if(v){
                    localStorage.setItem("vtypeBtns.vtype", v);
                }else{
                    localStorage.removeItem("vtypeBtns.vtype");
                }
                vtypeBtns.check(spanOuter);
                console.log(`vtypeBtns.vtype=${vtypeBtns.vtype}`);
            });
            
            if(v==vtypeBtns.vtype){
                btn.checked=true;
                vtypeBtns.check(spanOuter);
            }

            spanOuter.appendChild(btn);
            spanOuter.appendChild(span);
            vtypeBtns.appendChild(spanOuter);

        });


        needHeader.addEventListener("click", () => {
            needHeader.yes = !needHeader.yes;
            localStorage.setItem("needHeader.yes", needHeader.yes ? "yes" : "");
            needHeader.show();
        });
        needHeader.show();

        let handler = setInterval(()=>{
            upate_state();
        }, 5000);

        testYa = (t) => {
            let url1 = "http://x.tv.server.lan:5678/d/%E7%BA%AA%E5%BD%95%E7%89%87/%E8%87%AA%E7%84%B6/%E4%BA%BA%E7%B1%BB%E4%B8%96/%E4%BA%BA%E7%B1%BB%E4%B8%96.%E4%BA%BA%E7%B1%BB%E7%BA%AA.Anthropocene.the.Human.Epoch.2019.1080p.BluRay.x264.mkv";
            let vv = {
                pic: PIC,
                url: url1,
                type: t,
            };
            PLAYER.switchVideo(vv);
        }
        // testYa("dash");
    </script>
</body>

</html>